package com.cyh;

import java.util.concurrent.*;
import java.util.concurrent.locks.ReentrantReadWriteLock;

/**
 * JUC
 * AQS全称AbstractQueuedSynchronizer,是阻塞式锁和相关的同步器工具框架, 用的是park和unpark阻塞和释放。
 *    方法：tryAcquire:尝试获取锁
 *         tryRelease:尝试是否锁
 *         tryAcquireShared:
 *         tryReleaseShared:
 *         isHeldExclusively:
 *
 *
 *    ReentrantLock加锁前面已经学过
 *    ReentrantReadWriteLock: 读写加锁, 读读不互斥，读写和写写互斥：注意：已经是写锁，可以获得读锁，并等待写锁释放；已经是读锁，无法获得写锁，必须等待读锁释放才能获得写锁。
 *                            很常用，释放锁一定要放到finally中。
 *
 *   StampedLock: 优化上面的读写锁，特点：使用读锁和写锁是必须配合戳使用.缺点：不支持条件变量和不支持可重入。
 *        加解读锁：
 *            long stamp = lock.readLock();
 *            lock.unlockRead(stamp); //finally中调用
 *        加解写锁：
 *           long stamp = lock.writeLock();
 *           lock.unlockWrite(stamp); //finally中调用
 *        乐观读，StampedLock支持tryOptimisticRead()方法（乐观读），读取完毕后需要做一次戳校验，如果校验通过，表示这期间没有做写操作，
 *               数据可以安全使用，如果检验没有通过，需要重新获取读锁，保证数据安全
 *               long stamp = lock.tryOptimisticRead(); //这个地方没有加锁
 *               if(!lock.validate(stamp)){ //可以减少加一个读锁，只有为false时，才加读锁
 *                   //升级锁
 *                   //即加读锁
 *               }
 *
 *   Semaphore：信号量，用来限制能同时访问共享资源的线程上限
 *   CountdownLatch:线程倒计时锁，用来进行线程同步协作，等待所有线程完成倒计时。
 *        CountDownLatch cdl = new CountDownLatch(3); 主线程中
 *        新建三个线程，线程中最后一句代码，cdl.countDown(); 表示计数减1.
 *        主线程或其他线程 cdl.await(); 表示运行了三次countDown()后才运行下面的代码。
 *
 *        join也能达到上面的效果，但是CountdownLatch用起来更方便，而且还能在线程池中使用，join不适用线程池。
 *
 *   Future<Type> 线程或者线程池的submit方法的返回值， f.get() 获取线程池中线程的返回值（阻塞式）
 *
 *   CyclicBarrier功能与CountdownLatch类似，只是它表示可以循环使用多次。 看测试代码
 * */
public class Test04 {
    private static int data;
    private static ReentrantReadWriteLock rw = new ReentrantReadWriteLock();
    private static ReentrantReadWriteLock.ReadLock r = rw.readLock();
    private static ReentrantReadWriteLock.WriteLock w = rw.writeLock();

    public static void main(String[] args) {
        new Thread(() -> {
            read();
        }, "t1").start();
        new Thread(() -> {
            write(10);
        }, "t2").start();


        //测试信号量
        testSemaphore();

        testCyclicBarrier();
    }

    public static int read() {
        r.lock();
        try {
            return data;
        } finally {
            r.unlock();
        }
    }
    public static void write(int i) {
        w.lock();
        try{
            data = i;
        } finally {
            w.unlock();
        }
    }

    public static void testSemaphore() {
        Semaphore s = new Semaphore(3); //同时最大3个线程在s.acquire中
        for(int i = 0; i < 10; i++) {
            new Thread(() -> {
                try {
                    s.acquire();//获取信号量
                    Thread.sleep(3000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                } finally {
                    s.release(); //释放信号量
                }
            }).start();
        }
    }

    public static void testCyclicBarrier() {
        ExecutorService service = Executors.newFixedThreadPool(2);
        CyclicBarrier barrier = new CyclicBarrier(2, ()->{
            System.out.println("task1, 和task2运行结束1轮后，执行一次");
        }); //可以循环使用，循环得到结果， 此例循环了3轮。 注意：service,barrier和for循环里面的线程数要一致，此例都是2个。
        for(int i = 0; i < 3; i++) {
            service.submit(()->{
                System.out.println("test1 begin");
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                try {
                    barrier.await(); //barrier计数开始减1，如果不是0，则等待其他线程减1后变成0，才会向下运行。
                    System.out.println("task1 end");
                } catch (InterruptedException e) {
                    e.printStackTrace();
                } catch (BrokenBarrierException e) {
                    e.printStackTrace();
                }
            });

            service.submit(()->{
                System.out.println("test2 begin");
                try {
                    Thread.sleep(2000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                try {
                    barrier.await(); //barrier计数开始减1，如果不是0，则等待其他线程减1后变成0，才会向下运行。
                    System.out.println("task2 end");
                } catch (InterruptedException e) {
                    e.printStackTrace();
                } catch (BrokenBarrierException e) {
                    e.printStackTrace();
                }
            });
        }


    }
}
